package com.twod.feralvillage;

import com.twod.feralvillage.ChannelSystem.Channel;
import com.twod.feralvillage.GameObject.ActionType;

public class NPCAnimationComponent extends GameComponent {

    // Animations
    public static final int IDLE = 0;
    public static final int WALK = 1;
    public static final int RUN_START = 2;
    public static final int RUN = 3;
    public static final int SHOOT = 4;
    public static final int JUMP_START = 5;
    public static final int JUMP_AIR = 6;
    public static final int TAKE_HIT = 7;
    public static final int SURPRISED = 8;
    public static final int DEATH = 9;

   
    protected static final float RUN_SPEED_THRESHOLD = 100.0f;
    protected static final float JUMP_SPEED_THRESHOLD = 25.0f;
    protected static final float FALL_SPEED_THRESHOLD = -25.0f;
    protected static final float FALL_TIME_THRESHOLD = 0.2f;
   
    private int mCurrentAnimation;
    private SpriteComponent mSprite;
    private ChannelSystem.Channel mChannel;
    private int mChannelTrigger;
    private boolean mFlying;
    private boolean mStopAtWalls;       // Controls whether or not the character will go back
                                                                // to idle when running into a wall

   
    public NPCAnimationComponent() {
        super();
        reset();
        setPhase(GameComponent.ComponentPhases.ANIMATION.ordinal());
    }
   
    @Override
    public void reset() {
        mCurrentAnimation = IDLE;
        mChannel = null;
        mSprite = null;
        mFlying = false;
        mStopAtWalls = true;
    }
   
    @Override
    public void update(float timeDelta, BaseObject parent) {
        if (mSprite != null) {
            GameObject parentObject = (GameObject)parent;
           
            final int oldAnimation = mCurrentAnimation;
            switch(mCurrentAnimation) {
                case IDLE:
                    idle(parentObject);
                    break;
                case WALK:
                    walk(parentObject);
                    break;
                case RUN_START:
                    runStart(parentObject);
                    break;
                case RUN:
                    run(parentObject);
                    break;
                case SHOOT:
                    shoot(parentObject);
                    break;
                case JUMP_START:
                    jumpStart(parentObject);
                    break;
                case JUMP_AIR:
                    jumpAir(parentObject);
                    break;
                case TAKE_HIT:
                        takeHit(parentObject);
                        break;
                case SURPRISED:
                        surprised(parentObject);
                        break;
                case DEATH:
                        death(parentObject);
                        break;
               default:
                    assert(false);
            }
           
            if (mChannel != null) {
                if (mChannel.value != null
                                && ((ChannelSystem.ChannelBooleanValue)mChannel.value).value) {
                        mCurrentAnimation = mChannelTrigger;
                }
            }
           
            if (oldAnimation != mCurrentAnimation) {
                mSprite.playAnimation(mCurrentAnimation);
            }
        }
    }
   
    protected boolean shouldFall(GameObject parentObject) {
        boolean result = false;
        TimeSystem time = sSystemRegistry.timeSystem;
        final float airTime = time.getGameTime() - parentObject.getLastTouchedFloorTime();
        if (!mFlying && !parentObject.touchingGround() && airTime > FALL_TIME_THRESHOLD) {
            final Vector2 velocity = parentObject.getVelocity();
            if (velocity.y < FALL_SPEED_THRESHOLD) {
                result = true;
            }
        }
        return result;
    }
   
    protected boolean shouldJump(GameObject parentObject) {
        boolean result = false;
       
        if (!mFlying) {
                final Vector2 velocity = parentObject.getVelocity();
                if (velocity.y > JUMP_SPEED_THRESHOLD) {
                    result = true;
                }
        }
        return result;
    }
   
    protected boolean shouldRun(GameObject parentObject) {
        boolean result = false;
        if (!mFlying && parentObject.touchingGround()) {
            final Vector2 velocity = parentObject.getVelocity();
            if (Math.abs(velocity.x) >= RUN_SPEED_THRESHOLD) {
                result = true;
            }
        }
        return result;
    }
   
    protected boolean shouldMove(GameObject parentObject) {
        boolean result = true;
        final Vector2 velocity = parentObject.getVelocity();
       
        if (mStopAtWalls) {
                if ((velocity.x < 0.0f && parentObject.touchingLeftWall())
                        || (velocity.x > 0.0f && parentObject.touchingRightWall())) {
                    result = false;
                }
        }
        return result;
    }
   
    protected boolean shouldTakeHit(GameObject parentObject) {
        boolean result = false;
        if (parentObject.getCurrentAction() == ActionType.HIT_REACT
                        && mSprite.findAnimation(TAKE_HIT) != null) {
                result = true;
        }
        return result;
    }
   
    protected void gotoRunStart() {
        if (mSprite.findAnimation(RUN_START) != null) {
            mCurrentAnimation = RUN_START;
        } else {
            mCurrentAnimation = RUN;
        }
    }
   
    protected void gotoRun() {
        mCurrentAnimation = RUN;
    }
   
    protected void idle(GameObject parentObject) {
        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
        if (currentAction == ActionType.MOVE) {
            final Vector2 velocity = parentObject.getVelocity();
            if (shouldFall(parentObject)) {
                mCurrentAnimation = JUMP_AIR;
            } else if (shouldJump(parentObject)) {
                mCurrentAnimation = JUMP_START;
                parentObject.positionLocked = true;
            } else if (Math.abs(velocity.x) > 0.0f && shouldMove(parentObject)) {
                if (shouldRun(parentObject)) {
                        gotoRunStart();
                        parentObject.positionLocked = true;
                } else {
                    mCurrentAnimation = WALK;
                }
            }
        } else if (currentAction == ActionType.ATTACK) {
            mCurrentAnimation = SHOOT;
        } else if (shouldTakeHit(parentObject)) {
                mCurrentAnimation = TAKE_HIT;
        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
                mCurrentAnimation = DEATH;
        }
     }
   
    protected void walk(GameObject parentObject) {
        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
        if (currentAction == ActionType.MOVE) {
            final Vector2 velocity = parentObject.getVelocity();
            if (shouldFall(parentObject)) {
                mCurrentAnimation = JUMP_AIR;
            } else if (shouldJump(parentObject)) {
                mCurrentAnimation = JUMP_START;
                parentObject.positionLocked = true;
            } else if (Math.abs(velocity.x) > 0.0f) {
                if (shouldRun(parentObject)) {
                    gotoRun();
                }
                if (velocity.x > 0.0f) {
                    parentObject.facingDirection.x = 1;
                } else {
                    parentObject.facingDirection.x = -1;
                }
            } else {
                mCurrentAnimation = IDLE;
            }
        } else if (currentAction == ActionType.ATTACK) {
            mCurrentAnimation = SHOOT;
        } else if (shouldTakeHit(parentObject)) {
                mCurrentAnimation = TAKE_HIT;
        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
                mCurrentAnimation = DEATH;
        }
    }

    protected void runStart(GameObject parentObject) {
        parentObject.positionLocked = true;
        if (mSprite.animationFinished()) {
            mCurrentAnimation = RUN;
            parentObject.positionLocked = false;
        }
    }
    protected void run(GameObject parentObject) {
        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
        if (currentAction == ActionType.MOVE) {
            final Vector2 velocity = parentObject.getVelocity();
            if (shouldFall(parentObject)) {
                mCurrentAnimation = JUMP_AIR;
            } else if (shouldJump(parentObject)) {
                parentObject.positionLocked = true;
                mCurrentAnimation = JUMP_START;
            } else if (Math.abs(velocity.x) > 0.0f) {
                if (!shouldRun(parentObject)) {
                    mCurrentAnimation = WALK;
                }
               
                if (velocity.x > 0.0f) {
                    parentObject.facingDirection.x = 1;
                } else {
                    parentObject.facingDirection.x = -1;
                }
            } else {
                mCurrentAnimation = IDLE;
            }
        } else if (currentAction == ActionType.ATTACK) {
            mCurrentAnimation = SHOOT;
        } else if (shouldTakeHit(parentObject)) {
                mCurrentAnimation = TAKE_HIT;
        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
                mCurrentAnimation = DEATH;
        }
    }

    protected void shoot(GameObject parentObject) {
        if (mSprite.animationFinished() || parentObject.getCurrentAction() != ActionType.ATTACK) {
            mCurrentAnimation = IDLE;
        } else if (shouldTakeHit(parentObject)) {
                mCurrentAnimation = TAKE_HIT;
        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
                mCurrentAnimation = DEATH;
        } else {
            final Vector2 velocity = parentObject.getVelocity();

                if (velocity.x > 0.0f) {
                parentObject.facingDirection.x = 1;
            } else if (velocity.x < 0.0f) {
                parentObject.facingDirection.x = -1;
            }
        }
    }

    protected void jumpStart(GameObject parentObject) {
        final Vector2 velocity = parentObject.getVelocity();

        if (velocity.x > 0.0f) {
            parentObject.facingDirection.x = 1;
        } else if (velocity.x < 0.0f) {
            parentObject.facingDirection.x = -1;
        }
        parentObject.positionLocked = true;
       
        if (mSprite.animationFinished()) {
            mCurrentAnimation = JUMP_AIR;
            parentObject.positionLocked = false;
        }
    }
   
    protected void jumpAir(GameObject parentObject) {
        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
        if (currentAction == ActionType.MOVE) {
            final Vector2 velocity = parentObject.getVelocity();

            if (parentObject.touchingGround()) {
                if (Math.abs(velocity.x) > 0.0f) {
                    if (shouldRun(parentObject)) {
                        mCurrentAnimation = RUN;
                    } else {
                        mCurrentAnimation = WALK;
                    }
                } else {
                    mCurrentAnimation = IDLE;
                }
            } else {
               
                if (velocity.x > 0.0f) {
                    parentObject.facingDirection.x = 1;
                } else if (velocity.x < 0.0f) {
                    parentObject.facingDirection.x = -1;
                }
               
            }
        } else {
            mCurrentAnimation = IDLE;
        }
    }
     
    protected void takeHit(GameObject parentObject) {
        if (mSprite.animationFinished()) {
                if (parentObject.life > 0 && parentObject.getCurrentAction() != ActionType.DEATH) {
                        if (parentObject.getCurrentAction() != ActionType.HIT_REACT) {
                                mCurrentAnimation = IDLE;
                        }
                        } else {
                        mCurrentAnimation = DEATH;
                }
        }      
    }
   
    protected void surprised(GameObject parentObject) {
        if (mSprite.animationFinished()) {
                mCurrentAnimation = IDLE;
        }
    }
   
    protected void death(GameObject parentObject) {
    }
   
    public void setSprite(SpriteComponent sprite) {
        mSprite = sprite;
    }

        public void setChannel(Channel channel) {
                mChannel = channel;
        }

        public void setChannelTrigger(int animation) {
                mChannelTrigger = animation;
        }
       
        public void setFlying(boolean flying) {
                mFlying = flying;
        }
       
        public void setStopAtWalls(boolean stop) {
                mStopAtWalls = stop;
        }
}

